﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace HslCommunication.Algorithms.ConnectPool
{
	/// <summary>
	/// 一个连接池管理器，负责维护多个可用的连接，并且自动清理，扩容，用于快速读写服务器或是PLC时使用。<br />
	/// A connection pool manager is responsible for maintaining multiple available connections, 
	/// and automatically cleans up, expands, and is used to quickly read and write servers or PLCs.
	/// </summary>
	/// <typeparam name="TConnector">管理的连接类，需要支持IConnector接口</typeparam>
	/// <remarks>
	/// 需要先实现 <see cref="T:HslCommunication.Algorithms.ConnectPool.IConnector" /> 接口的对象，然后就可以实现真正的连接池了，理论上可以实现任意的连接对象，包括modbus连接对象，各种PLC连接对象，数据库连接对象，redis连接对象，SimplifyNet连接对象等等。下面的示例就是modbus-tcp的实现
	/// <note type="warning">要想真正的支持连接池访问，还需要服务器支持一个端口的多连接操作，三菱PLC的端口就不支持，如果要测试示例代码的连接池对象，需要使用本组件的<see cref="T:HslCommunication.ModBus.ModbusTcpServer" />来创建服务器对象</note>
	/// </remarks>
	/// <example>
	/// 下面举例实现一个modbus的连接池对象，先实现接口化的操作
	/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Algorithms\ConnectPool.cs" region="IConnector Example" title="IConnector示例" />
	/// 然后就可以实现真正的连接池了
	/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Algorithms\ConnectPool.cs" region="ConnectPoolExample" title="ConnectPool示例" />
	/// </example>
	// Token: 0x020001A3 RID: 419
	public class ConnectPool<TConnector> where TConnector : IConnector
	{
		/// <summary>
		/// 实例化一个连接池对象，需要指定如果创建新实例的方法<br />
		/// To instantiate a connection pool object, you need to specify how to create a new instance
		/// </summary>
		/// <param name="createConnector">创建连接对象的委托</param>
		// Token: 0x0600212D RID: 8493 RVA: 0x000A151C File Offset: 0x0009F71C
		public ConnectPool(Func<TConnector> createConnector)
		{
			this.CreateConnector = createConnector;
			this.listLock = new object();
			this.connectors = new List<TConnector>();
			this.timerCheck = new Timer(new TimerCallback(this.TimerCheckBackground), null, 10000, 30000);
		}

		/// <summary>
		/// 获取一个可用的连接对象，如果已经达到上限，就进行阻塞等待。当使用完连接对象的时候，需要调用<see cref="M:HslCommunication.Algorithms.ConnectPool.ConnectPool`1.ReturnConnector(`0)" />方法归还连接对象。<br />
		/// Get an available connection object, if the upper limit has been reached, block waiting. When the connection object is used up, 
		/// you need to call the <see cref="M:HslCommunication.Algorithms.ConnectPool.ConnectPool`1.ReturnConnector(`0)" /> method to return the connection object.
		/// </summary>
		/// <returns>可用的连接对象</returns>
		// Token: 0x0600212E RID: 8494 RVA: 0x000A15AC File Offset: 0x0009F7AC
		public TConnector GetAvailableConnector()
		{
			while (!this.canGetConnector)
			{
				Thread.Sleep(20);
			}
			TConnector tconnector = default(TConnector);
			object obj = this.listLock;
			lock (obj)
			{
				for (int i = 0; i < this.connectors.Count; i++)
				{
					TConnector tconnector2 = this.connectors[i];
					bool flag2 = !tconnector2.IsConnectUsing;
					if (flag2)
					{
						tconnector2 = this.connectors[i];
						tconnector2.IsConnectUsing = true;
						tconnector = this.connectors[i];
						break;
					}
				}
				bool flag3 = tconnector == null;
				if (flag3)
				{
					tconnector = this.CreateConnector();
					tconnector.IsConnectUsing = true;
					tconnector.LastUseTime = DateTime.Now;
					tconnector.Open();
					this.connectors.Add(tconnector);
					this.usedConnector = this.connectors.Count;
					bool flag4 = this.usedConnector > this.usedConnectorMax;
					if (flag4)
					{
						this.usedConnectorMax = this.usedConnector;
					}
					bool flag5 = this.usedConnector == this.maxConnector;
					if (flag5)
					{
						this.canGetConnector = false;
					}
				}
				tconnector.LastUseTime = DateTime.Now;
			}
			return tconnector;
		}

		/// <summary>
		/// 使用完之后需要通知连接池的管理器，本方法调用之前需要获取到连接对象信息。<br />
		/// After using it, you need to notify the manager of the connection pool, and you need to get the connection object information before calling this method.
		/// </summary>
		/// <param name="connector">连接对象</param>
		// Token: 0x0600212F RID: 8495 RVA: 0x000A1754 File Offset: 0x0009F954
		public void ReturnConnector(TConnector connector)
		{
			object obj = this.listLock;
			lock (obj)
			{
				int num = this.connectors.IndexOf(connector);
				bool flag2 = num != -1;
				if (flag2)
				{
					TConnector tconnector = this.connectors[num];
					tconnector.IsConnectUsing = false;
				}
			}
		}

		/// <summary>
		/// 将目前连接中的所有对象进行关闭，然后移除队列。<br />
		/// Close all objects in the current connection, and then remove the queue.
		/// </summary>
		// Token: 0x06002130 RID: 8496 RVA: 0x000A17CC File Offset: 0x0009F9CC
		public void ResetAllConnector()
		{
			object obj = this.listLock;
			lock (obj)
			{
				for (int i = this.connectors.Count - 1; i >= 0; i--)
				{
					TConnector tconnector = this.connectors[i];
					tconnector.Close();
					this.connectors.RemoveAt(i);
				}
			}
		}

		/// <summary>
		/// 获取或设置最大的连接数，当实际的连接数超过最大的连接数的时候，就会进行阻塞，直到有新的连接对象为止。<br />
		/// Get or set the maximum number of connections. When the actual number of connections exceeds the maximum number of connections, 
		/// it will block until there is a new connection object.
		/// </summary>
		// Token: 0x170006C4 RID: 1732
		// (get) Token: 0x06002131 RID: 8497 RVA: 0x000A1858 File Offset: 0x0009FA58
		// (set) Token: 0x06002132 RID: 8498 RVA: 0x000A1870 File Offset: 0x0009FA70
		public int MaxConnector
		{
			get
			{
				return this.maxConnector;
			}
			set
			{
				this.maxConnector = value;
			}
		}

		/// <summary>
		/// 获取或设置当前连接过期的时间，单位秒，默认30秒，也就是说，当前的连接在设置的时间段内未被使用，就进行释放连接，减少内存消耗。<br />
		/// Get or set the expiration time of the current connection, in seconds, the default is 30 seconds, that is, 
		/// if the current connection is not used within the set time period, the connection will be released to reduce memory consumption.
		/// </summary>
		// Token: 0x170006C5 RID: 1733
		// (get) Token: 0x06002133 RID: 8499 RVA: 0x000A187C File Offset: 0x0009FA7C
		// (set) Token: 0x06002134 RID: 8500 RVA: 0x000A1894 File Offset: 0x0009FA94
		public int ConectionExpireTime
		{
			get
			{
				return this.expireTime;
			}
			set
			{
				this.expireTime = value;
			}
		}

		/// <summary>
		/// 当前已经使用的连接数，会根据使用的频繁程度进行动态的变化。<br />
		/// The number of currently used connections will dynamically change according to the frequency of use.
		/// </summary>
		// Token: 0x170006C6 RID: 1734
		// (get) Token: 0x06002135 RID: 8501 RVA: 0x000A189E File Offset: 0x0009FA9E
		public int UsedConnector
		{
			get
			{
				return this.usedConnector;
			}
		}

		/// <summary>
		/// 当前已经使用的连接数的峰值，可以用来衡量当前系统的适用的连接池上限。<br />
		/// The current peak value of the number of connections used can be used to measure the upper limit of the applicable connection pool of the current system.
		/// </summary>
		// Token: 0x170006C7 RID: 1735
		// (get) Token: 0x06002136 RID: 8502 RVA: 0x000A18A6 File Offset: 0x0009FAA6
		public int UseConnectorMax
		{
			get
			{
				return this.usedConnectorMax;
			}
		}

		// Token: 0x06002137 RID: 8503 RVA: 0x000A18B0 File Offset: 0x0009FAB0
		private void TimerCheckBackground(object obj)
		{
			object obj2 = this.listLock;
			lock (obj2)
			{
				for (int i = this.connectors.Count - 1; i >= 0; i--)
				{
					DateTime now = DateTime.Now;
					TConnector tconnector = this.connectors[i];
					bool flag2;
					if ((now - tconnector.LastUseTime).TotalSeconds > (double)this.expireTime)
					{
						tconnector = this.connectors[i];
						flag2 = !tconnector.IsConnectUsing;
					}
					else
					{
						flag2 = false;
					}
					bool flag3 = flag2;
					if (flag3)
					{
						tconnector = this.connectors[i];
						tconnector.Close();
						this.connectors.RemoveAt(i);
					}
				}
				this.usedConnector = this.connectors.Count;
				bool flag4 = this.usedConnector < this.MaxConnector;
				if (flag4)
				{
					this.canGetConnector = true;
				}
			}
		}

		// Token: 0x040007EA RID: 2026
		private Func<TConnector> CreateConnector = null;

		// Token: 0x040007EB RID: 2027
		private int maxConnector = 10;

		// Token: 0x040007EC RID: 2028
		private int usedConnector = 0;

		// Token: 0x040007ED RID: 2029
		private int usedConnectorMax = 0;

		// Token: 0x040007EE RID: 2030
		private int expireTime = 30;

		// Token: 0x040007EF RID: 2031
		private bool canGetConnector = true;

		// Token: 0x040007F0 RID: 2032
		private Timer timerCheck = null;

		// Token: 0x040007F1 RID: 2033
		private object listLock;

		// Token: 0x040007F2 RID: 2034
		private List<TConnector> connectors = null;
	}
}
